#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>
#include <adminmenu>

public Plugin myinfo = 
{
    name        = "l4d2_Player_Flight_Mode",
    author      = "豆瓣酱な",
    description = "管理员菜单设置生还者飞行.",
    version     = "1.0",
    url         = "https://gitee.com/sweet-and-sour-bombs/adfvsdsd",
}

bool g_bFlyMode[MAXPLAYERS+1];
Handle g_hFlyTimer[MAXPLAYERS+1];
Handle g_hTopMenu = null;

ConVar g_cvFlySpeed;
ConVar g_cvFlyVerticalSpeed;
ConVar g_cvFlySuperJump;
ConVar g_cvSaveState;

float g_fFlySpeed;
float g_fFlyVerticalSpeed;
float g_fFlySuperJump;
bool g_bSaveState;

public void OnPluginStart()
{
    g_cvFlySpeed = CreateConVar("l4d2_fly_speed", "600.0", "水平飞行速度", FCVAR_NOTIFY, true, 0.0);
    g_cvFlyVerticalSpeed = CreateConVar("l4d2_fly_vertical_speed", "150.0", "垂直飞行速度", FCVAR_NOTIFY, true, 0.0);
    g_cvFlySuperJump = CreateConVar("l4d2_fly_super_jump", "400.0", "超级跳跃高度", FCVAR_NOTIFY, true, 0.0);
    g_cvSaveState = CreateConVar("l4d2_fly_save_state", "1", "是否保存飞行状态到下一关/换图", FCVAR_NOTIFY, true, 0.0, true, 1.0);
    
    GetConVarValues();
    
    g_cvFlySpeed.AddChangeHook(OnConVarChanged);
    g_cvFlyVerticalSpeed.AddChangeHook(OnConVarChanged);
    g_cvFlySuperJump.AddChangeHook(OnConVarChanged);
    g_cvSaveState.AddChangeHook(OnConVarChanged);
    
    RegAdminCmd("sm_fly", Command_ToggleFly, ADMFLAG_CHEATS, "切换玩家飞行模式");
    
    HookEvent("player_jump", Event_SuperJump, EventHookMode_Pre);
    HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
    HookEvent("player_spawn", Event_PlayerSpawn, EventHookMode_Post);
    HookEvent("map_transition", Event_MapTransition, EventHookMode_PostNoCopy);
    HookEvent("mission_lost", Event_MissionLost, EventHookMode_PostNoCopy);
    
    AddCommandListener(Command_OnMapEnd, "changelevel");
    AddCommandListener(Command_OnMapEnd, "map");
    
    LoadAdminMenu();
}

void LoadAdminMenu()
{
    Handle topmenu;
    if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != null))
    {
        OnAdminMenuReady(topmenu);
    }
}

public void OnAdminMenuReady(Handle topmenu)
{
    if (topmenu == g_hTopMenu)
        return;
    
    g_hTopMenu = topmenu;
    
    TopMenuObject player_commands = FindTopMenuCategory(g_hTopMenu, ADMINMENU_PLAYERCOMMANDS);
    
    if (player_commands != INVALID_TOPMENUOBJECT)
    {
        AddToTopMenu(g_hTopMenu,
            "sm_playerflight",
            TopMenuObject_Item,
            AdminMenu_FlightHandler,
            player_commands,
            "sm_playerflight",
            ADMFLAG_CHEATS);
    }
}


public void AdminMenu_FlightHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
    if (action == TopMenuAction_DisplayOption)
    {
        Format(buffer, maxlength, "玩家飞行模式");
    }
    else if (action == TopMenuAction_SelectOption)
    {
        DisplayFlightPlayerMenu(param);
    }
}

void DisplayFlightPlayerMenu(int client)
{
    Menu menu = new Menu(MenuHandler_FlightPlayers);
    menu.SetTitle("选择生还者:");
    
    // 添加全选功能按钮
    menu.AddItem("all_on", ">>> 开启全部玩家飞行 <<<");
    menu.AddItem("all_off", ">>> 关闭全部玩家飞行 <<<");
    menu.AddItem("", "", ITEMDRAW_SPACER); // 分隔线
    
    char userid[12], name[MAX_NAME_LENGTH], display[64];
    for (int i = 1; i <= MaxClients; i++)
    {
        if (IsClientInGame(i) && GetClientTeam(i) == 2)
        {
            IntToString(GetClientUserId(i), userid, sizeof(userid));
            GetClientName(i, name, sizeof(name));
            
            Format(display, sizeof(display), "%s %s", name, g_bFlyMode[i] ? "[飞行中]" : "");
            menu.AddItem(userid, display);
        }
    }
    
    menu.ExitBackButton = true;
    menu.Display(client, MENU_TIME_FOREVER);
}

public int MenuHandler_FlightPlayers(Menu menu, MenuAction action, int param1, int param2)
{
    if (action == MenuAction_Select)
    {
        char info[32];
        menu.GetItem(param2, info, sizeof(info));
        
        // 处理全选功能
        if (StrEqual(info, "all_on"))
        {
            int count = 0;
            for (int i = 1; i <= MaxClients; i++)
            {
                if (IsClientInGame(i) && GetClientTeam(i) == 2 && !g_bFlyMode[i])
                {
                    TogglePlayerFlight(i, param1, false);
                    count++;
                }
            }
            if (count > 0)
            {
                PrintToChatAll("\x04[系统]\x01 管理员已为\x05所有生还者\x01开启飞行模式");
            }
            DisplayFlightPlayerMenu(param1);
        }
        else if (StrEqual(info, "all_off"))
        {
            int count = 0;
            for (int i = 1; i <= MaxClients; i++)
            {
                if (IsClientInGame(i) && GetClientTeam(i) == 2 && g_bFlyMode[i])
                {
                    TogglePlayerFlight(i, param1, false);
                    count++;
                }
            }
            if (count > 0)
            {
                PrintToChatAll("\x04[系统]\x01 管理员已为\x05所有生还者\x01关闭飞行模式");
            }
            DisplayFlightPlayerMenu(param1);
        }
        else
        {
            int userid = StringToInt(info);
            int target = GetClientOfUserId(userid);
            
            if (target && IsClientInGame(target) && GetClientTeam(target) == 2)
            {
                TogglePlayerFlight(target, param1);
                DisplayFlightPlayerMenu(param1);
            }
            else
            {
                PrintToChat(param1, "\x04[系统]\x01 目标玩家无效或已离开");
                if (g_hTopMenu != null)
                {
                    DisplayTopMenu(g_hTopMenu, param1, TopMenuPosition_LastCategory);
                }
            }
        }
    }
    else if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack)
    {
        if (g_hTopMenu != null)
        {
            DisplayTopMenu(g_hTopMenu, param1, TopMenuPosition_LastCategory);
        }
    }
    else if (action == MenuAction_End)
    {
        delete menu;
    }
    
    return 0;
}

public Action Command_ToggleFly(int client, int args)
{
    if (args < 1)
    {
        ReplyToCommand(client, "[SM] 用法: sm_fly <玩家名|#userid|@all>");
        return Plugin_Handled;
    }
    
    char arg[65];
    GetCmdArg(1, arg, sizeof(arg));
    
    char target_name[MAX_TARGET_LENGTH];
    int target_list[MAXPLAYERS], target_count;
    bool tn_is_ml;
    
    if ((target_count = ProcessTargetString(
            arg,
            client,
            target_list,
            MAXPLAYERS,
            COMMAND_FILTER_NO_BOTS,
            target_name,
            sizeof(target_name),
            tn_is_ml)) <= 0)
    {
        ReplyToTargetError(client, target_count);
        return Plugin_Handled;
    }
    
    int successCount = 0;
    for (int i = 0; i < target_count; i++)
    {
        if (GetClientTeam(target_list[i]) == 2)
        {
            TogglePlayerFlight(target_list[i], client);
            successCount++;
        }
    }
    
    if (successCount > 0)
    {
        ShowActivity2(client, "[SM] ", "%t %s的飞行模式", 
            g_bFlyMode[target_list[0]] ? "开启" : "关闭", target_name);
    }
    else
    {
        ReplyToCommand(client, "[SM] 没有找到有效的生还者目标");
    }
    
    return Plugin_Handled;
}

void TogglePlayerFlight(int client, int admin, bool showMessage = true)
{
    if (!IsValidClient(client) || GetClientTeam(client) != 2)
        return;
    
    g_bFlyMode[client] = !g_bFlyMode[client];
    
    if (g_bFlyMode[client])
    {
        if (IsPlayerAlive(client))
        {
            StartFlying(client);
        }
        if (showMessage && !IsFakeClient(client))
        {
            PrintToChat(client, "\x04[飞行模式]\x01 管理员已为你\x05开启\x01飞行模式");
            PrintToChat(client, "\x04[控制说明]\x01 方向键移动 | 空格上升 | 蹲下下降");
            PrintToChat(client, "\x04[当前速度]\x01 水平: \x05%.1f\x01 | 垂直: \x05%.1f", g_fFlySpeed, g_fFlyVerticalSpeed);
        }
    }
    else
    {
        StopFlying(client);
        if (showMessage && !IsFakeClient(client))
        {
            PrintToChat(client, "\x04[飞行模式]\x01 管理员已为你\x05关闭\x01飞行模式");
        }
    }
    
    if (showMessage && admin != client && !IsFakeClient(admin))
    {
        char adminName[MAX_NAME_LENGTH], targetName[MAX_NAME_LENGTH];
        GetClientName(admin, adminName, sizeof(adminName));
        GetClientName(client, targetName, sizeof(targetName));
        PrintToChatAll("\x04[系统]\x01 管理员 \x05%s\x01 %s了 \x05%s\x01 的飞行模式", 
            adminName, g_bFlyMode[client] ? "开启" : "关闭", targetName);
    }
}

void GetConVarValues()
{
    g_fFlySpeed = g_cvFlySpeed.FloatValue;
    g_fFlyVerticalSpeed = g_cvFlyVerticalSpeed.FloatValue;
    g_fFlySuperJump = g_cvFlySuperJump.FloatValue;
    g_bSaveState = g_cvSaveState.BoolValue;
}

public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
    GetConVarValues();
}

public Action Command_OnMapEnd(int client, const char[] command, int argc)
{
    if (g_bSaveState)
    {
        return Plugin_Continue;
    }
    ResetAllFlightStates();
    return Plugin_Continue;
}

public void OnClientDisconnect(int client)
{
    if (!g_bSaveState)
    {
        StopFlying(client);
        g_bFlyMode[client] = false;
    }
}

public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
{
    if (g_bSaveState)
    {
        for(int i = 1; i <= MaxClients; i++)
        {
            if(g_bFlyMode[i] && IsClientInGame(i) && GetClientTeam(i) == 2)
            {
                CreateTimer(3.0, Timer_DelayedRoundStart, GetClientUserId(i), TIMER_FLAG_NO_MAPCHANGE);
            }
        }
    }
}

public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    if(IsValidClient(client) && g_bFlyMode[client] && GetClientTeam(client) == 2)
    {
        CreateTimer(0.5, Timer_DelayedSpawnCheck, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
    }
}

public void Event_MapTransition(Event event, const char[] name, bool dontBroadcast)
{
    if (g_bSaveState)
    {
        for(int i = 1; i <= MaxClients; i++)
        {
            if(g_bFlyMode[i] && IsClientInGame(i) && GetClientTeam(i) == 2)
            {
                CreateTimer(5.0, Timer_DelayedMapTransition, GetClientUserId(i), TIMER_FLAG_NO_MAPCHANGE);
            }
        }
    }
}

public void Event_MissionLost(Event event, const char[] name, bool dontBroadcast)
{
    // 团灭后不重置飞行状态
    if (!g_bSaveState)
    {
        ResetAllFlightStates();
    }
}

void ResetAllFlightStates()
{
    for(int i = 1; i <= MaxClients; i++)
    {
        StopFlying(i);
        g_bFlyMode[i] = false;
    }
}

public Action Timer_DelayedSpawnCheck(Handle timer, int userid)
{
    int client = GetClientOfUserId(userid);
    if(client && IsClientInGame(client) && GetClientTeam(client) == 2 && IsPlayerAlive(client) && g_bFlyMode[client])
    {
        StartFlying(client);
        if (!IsFakeClient(client))
        {
            PrintToChat(client, "\x04[飞行模式]\x01 已自动恢复飞行状态");
        }
    }
    return Plugin_Stop;
}

public Action Timer_DelayedRoundStart(Handle timer, int userid)
{
    int client = GetClientOfUserId(userid);
    if(client && IsClientInGame(client) && GetClientTeam(client) == 2 && g_bFlyMode[client])
    {
        if(IsPlayerAlive(client))
        {
            StartFlying(client);
            if (!IsFakeClient(client))
            {
                PrintToChat(client, "\x04[飞行模式]\x01 已自动恢复飞行状态");
            }
        }
    }
    return Plugin_Stop;
}

public Action Timer_DelayedMapTransition(Handle timer, int userid)
{
    int client = GetClientOfUserId(userid);
    if(client && IsClientInGame(client) && GetClientTeam(client) == 2 && g_bFlyMode[client])
    {
        if(IsPlayerAlive(client))
        {
            StartFlying(client);
            if (!IsFakeClient(client))
            {
                PrintToChat(client, "\x04[飞行模式]\x01 已自动恢复飞行状态");
            }
        }
    }
    return Plugin_Stop;
}

void StartFlying(int client)
{
    if(!IsValidClient(client) || !IsPlayerAlive(client) || GetClientTeam(client) != 2)
        return;
        
    StopFlying(client);
    SetEntityGravity(client, 0.001);
    g_hFlyTimer[client] = CreateTimer(0.1, Timer_FlyControl, client, TIMER_REPEAT);
}

void StopFlying(int client)
{
    if(!IsValidClient(client))
        return;
        
    if(g_hFlyTimer[client] != null)
    {
        KillTimer(g_hFlyTimer[client]);
        g_hFlyTimer[client] = null;
    }
    SetEntityGravity(client, 1.0);
    
    float vel[3] = {0.0, 0.0, 0.0};
    TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vel);
}

public Action Timer_FlyControl(Handle timer, int client)
{
    if(IsValidClient(client) && IsPlayerAlive(client) && GetClientTeam(client) == 2 && g_bFlyMode[client])
    {
        int buttons = GetClientButtons(client);
        float vel[3] = {0.0, 0.0, 0.0};
        
        if(buttons & (IN_FORWARD|IN_BACK|IN_MOVELEFT|IN_MOVERIGHT|IN_JUMP|IN_DUCK))
        {
            float ang[3], vecForward[3], vecRight[3];
            
            GetClientEyeAngles(client, ang);
            GetAngleVectors(ang, vecForward, vecRight, NULL_VECTOR);
            
            vecForward[2] = vecRight[2] = 0.0;
            NormalizeVector(vecForward, vecForward);
            NormalizeVector(vecRight, vecRight);
            
            if(buttons & IN_FORWARD)  
            {
                vel[0] += vecForward[0] * g_fFlySpeed;
                vel[1] += vecForward[1] * g_fFlySpeed;
            }
            if(buttons & IN_BACK)     
            {
                vel[0] -= vecForward[0] * g_fFlySpeed;
                vel[1] -= vecForward[1] * g_fFlySpeed;
            }
            if(buttons & IN_MOVELEFT)  
            {
                vel[0] -= vecRight[0] * g_fFlySpeed;
                vel[1] -= vecRight[1] * g_fFlySpeed;
            }
            if(buttons & IN_MOVERIGHT) 
            {
                vel[0] += vecRight[0] * g_fFlySpeed;
                vel[1] += vecRight[1] * g_fFlySpeed;
            }
            if(buttons & IN_JUMP)      vel[2] = g_fFlyVerticalSpeed;
            if(buttons & IN_DUCK)      vel[2] = -g_fFlyVerticalSpeed;
        }
        
        TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vel);
    }
    return Plugin_Continue;
}

public Action Event_SuperJump(Event event, const char[] name, bool dontBroadcast)
{
    int client = GetClientOfUserId(event.GetInt("userid"));
    if(IsValidClient(client) && IsPlayerAlive(client) && GetClientTeam(client) == 2 && g_bFlyMode[client])
    {
        float vel[3];
        GetEntPropVector(client, Prop_Data, "m_vecVelocity", vel);
        vel[2] = g_fFlySuperJump;
        TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, vel);
        return Plugin_Handled;
    }
    return Plugin_Continue;
}

bool IsValidClient(int client)
{
    return (client > 0 && client <= MaxClients && IsClientInGame(client));
}